<?php
/**
 * Created by PhpStorm.
 * User: Inhere
 * Date: 2015/2/26
 * Time: 20:05
 * Use :
 * File: AbstractModule.php
 */

namespace ulue\core\module;

use Ioc;
use Ulue,
    Debug;

use ulue\base\application\WebApplication;
use ulue\core\abstracts\AbstractBase;
use ulue\core\router\AbstractRouter;
use ulue\core\TheSystemException;
use ulue\libs\helpers\ArrayHelper;
use ulue\libs\traits\TraitGetOption;

class AbstractModule extends AbstractBase
{
    /**
     * current Module's parent module
     * @var null|AbstractModule
     */
    private $parent = null;

    /**
     * 当前请求的控制器实例
     * @var \ulue\core\controller\AbstractController
     */
    private $controller;

    public $layout = null;

    public $theme = null;

    public $defaultController = 'default';
    public $defaultAction     = 'index';

    /**
     * 自动发布资源
     * @var boolean
     */
    protected $autoPublishAsset = true;

    /**
     *  [
     *      'submodule1',
     *      'submodule2' => ['aa'=>343434],
     *  ]
     * @var array
     */
    protected $modules   = [];

    public function __construct(array $options=[] , self $parent=null)
    {
        $this->parent = $parent;

        parent::__construct($options);
    }

    /**
     * 覆盖 init() 时,请记得先 调用 parent::init(); 以防止出现错误
     * @return void
     */
    protected function init()
    {
        // 若控制器中没有设置 layout theme .. 等属性,则读取 Application 中配置的默认值
        $app = Ulue::$app;

        !$this->defaultController && $this->defaultController = $app->defaultController;
        !$this->defaultAction     && $this->defaultAction     = $app->defaultAction;
        !$this->layout            && $this->layout            = $app->layout;
        !$this->theme             && $this->theme             = $app->theme;

        unset($app);
    }

    /**
     * filters filters 过滤请求
     * @inherit AbstractBase::filters()
     * @return array
     */
    public function filters()
    {
        /*
        return [
            'method'  => 'GET,POST, ...',
            # Only http & https
            'scheme'  =>  'https',
            'post'    =>  '80',
            'sslPort' =>  '443',

            'allow'   => 'controller1,controller2, ...',
            'users'   => 'user1,user2, ...',
            'roles'   => 'admin,editor, ...',
        ]
         */
        return [];
    }

    /**
     * [accessRules 访问权限、角色控制]
     * @return array
     */
    public function accessRules()
    {
        /*
        return [
          'allow' => 'method1,method2, ...',
          'users' => 'user1,user2, ...',
          'roles' => 'admin,editor, ...',
        ]
         */
        return [];
    }

    protected function accessValidate()
    {

    }

    /**
     * 检查当前控制器是否在配置文件设置了动作绑定到类
     * @param $routeStr
     * @return bool
     */
    // protected function isSetActionBindToClass($routeStr)
    // {
    //     $routeStr = trim($routeStr, '/ ');
    //     $setting  = Ulue::$app->actionBindToClass;

    //     return in_array($routeStr, (array) $setting);
    // }

    /**
     * 设置父级模块
     * @return array
     */
    public function setParent(AbstractModule $value)
    {
        $this->parent = $value;

        return $this;
    }

    /**
     * 获取父级模块
     * @return array
     */
    public function getParent()
    {
        return $this->parent;
    }

    /**
     * 发布资源到 可访问的资源目录 @web/static
     */
    public function publishAsset()
    {
        return $this;
        /*if ( $this->name != \Ulue::$app->defaultModule )
        {
            $options = (array) $this->config->get('asset.publish');
            $options['path'] = $this->path;
            $options['name'] = $this->name;

            $publish = new AssetPublish($options);
            $publish->execute();
        }

        return $this;*/
    }

    public function setModules(array $modules)
    {
        $this->modules = $modules;

        return $this;
    }

    public function getModules()
    {
        return $this->modules;
    }

//////////////////////////////// Submodule ////////////////////////////////

    public function hasSubmodule($name)
    {
        return isset($this->modules[$name]);
    }

    /**
     * 激活当前模块的子模块
     * @author inhere
     * @date   2015-08-14
     * @param  string         $mRoute
     * @return AbstractModule
     */
    public function activeSubmodule($mRoute)
    {
        if ( strpos($mRoute, '/') ) {
            list($name, $mRoute)  = explode('/', $mRoute, 2);
        } else {
            $name = $mRoute;
            $mRoute = null;
        }

        // 没有可用子模块了
        if ( !$this->hasSubmodule($name) ) {
            return $this;
        }

        $config = $this->modules[$name];

        // 自定义模块类
        if (!empty($config['class'])) {
            $moduleClass = $config['class'];
            $namespace   = self::spaceName($moduleClass);

            unset($config['class']);

        // 模块在默认目录
        } else {
            $namespace      = self::spaceName() . '\\' . $name;
            $className      = ucfirst($name) . Ulue::$app->suffix['module'];
            $moduleClass    = $namespace .'\\'.ucfirst($className);
            $config['path'] = $this->path . '/modules/' . $name;
        }

        if (!empty($config['path'])) {
            $config['path']  = Ulue::resolveAlias($config['path']);
            Ioc::$box->loader->setNs($namespace, $config['path'] );
        }

        $config['name']  = $name;
        $config['route'] = $this->route . '/'.$name;

        /**
         * @var $module \ulue\core\module\AbstractModule
         */
        $module = new $moduleClass( $config, $this );

        // 还有子模块请求， 继续激活子模块
        if ($mRoute) {
            $module = $module->activeSubmodule($mRoute);
        }

        unset($moduleClass, $config, $temp, $mRoute);

        return $module;
    }

/////////////////////////////// Controller ///////////////////////////////

    /**
     * 激活当前模块的控制器
     * @author inhere
     * @date   2015-08-22
     * @param  string     $cRoute
     * @return string
     */
    public function activeController($cRoute)
    {
        $name   = $cRoute ? : $this->defaultController;

        Debug::trace("i 根据请求的控制器路由 {$name},获取控制器对象并返回",
            "@param \$name = {$name}"
        );

        // 命名空间跟着模块走，因为模块不一定在 src 目录
        $app       = Ulue::$app;
        $suffix    = $app->suffix['controller'];
        $dirname   = Ioc::$box->pathManager->dir('controllers');
        $namespace = self::spaceName() . "\\$dirname";
        $pathTpl   = $this->path . "/{$dirname}/%s.php";
        $cRoute    = $name;

        if ( strpos($name, '/') ) {
            list($name, $other) = explode('/', $name, 2);
            $className = ucfirst($name).$suffix;
            $fullPath  = sprintf($pathTpl, $className);
            $cRoute    = $name;

            // 不存在，继续查找子目录是否有控制器
            if ( !file_exists($fullPath) && $other ) {
                if ( strpos($other, '/') ) {
                    list($subName, $other) = explode('/', $other, 2);
                } else {
                    $subName = $other;
                    $other   = '';
                }

                $className = $name . '\\' . ucfirst($subName).$suffix;
                $fullPath  = sprintf($pathTpl, str_replace('\\', '/', $className) );
                $cRoute    = $name . '/' . $subName;
                $name      = $subName;
            }
        } else {
            $className = ucfirst($name).$suffix;
            $fullPath  = sprintf($pathTpl, $className);
            $other     = '';
        }

        // 控制器文件检查(ps:使用默认控制器时)
        if (!file_exists($fullPath) ) {
            throw new \DRuntimeException(Ulue::tl('Requested file [{file}] does not exist!',['{file}'=>$fullPath]));
        }

        // 组装含命名空间的 完整控制器类名 admin\controllers\DefaultController
        $fullClass   = $namespace . '\\' . $className;
        $parentClass = '\ulue\core\controller\AbstractWebController';
        $config      = [
            // 'id'    => $fullClass,
            'path'  => dirname($fullPath),
            'name'  => $name,
            'route' => $this->route .'/'.$cRoute,
        ];

        /**
         * 实例化控制类
         * @var $controller \ulue\core\controller\AbstractWebController
         */
        $this->controller  = new $fullClass($config);
        $app->setController($this->controller);

        // $controller  = new $fullClass($config);
        // $app->setController($controller);

        unset($app,$className,$dirname,$fullClass,$fullPath);

        return $other;
    }

}

